#!/usr/bin/perl
use strict;
use warnings;

use lib '../lib';
use lib '../extlib';
use ChessLike::Board;
use ChessLike::GamePiece;

use List::MoreUtils qw( true firstidx );

use Test::Exception;
use Test::More 'no_plan';

my ( $board, $base, $flying_queen, $space_pawn, $action,
     @actions, @legal_actions, $gamepiece, $links_ref,
     $tentative_links_ref, $gamepieces_ref, $directions_ref );

################################
### Moving Linked Units Pt.1 ###
################################

print "\n*** Testing linked unit movement ***\n";

# This suite of tests was added in response to a couple of bugs
# in movement space calculation that were revealed only after moving
# a flier between carriers.

#########################
#   #   #   #   #   #   #
#########################
#   #   #   #   #   #   #
#########################
#   #   #   #   #^b2#   #
#########################
#   #   #   #   #   #   #
#########################
#   #   #   #   #   #   #
#########################
#   #   #   #   #^b1#   #
#########################
#   #   #   #   # f1#   #
#########################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 6, 'y-tiles' => 7 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);

$board->add(
   'e1',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'e2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'base.gif',
);
$board->add(
   'e5',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'base.gif',
);

@legal_actions = $board->legal_actions( 'e1', 0 );
@actions = grep { $_->to_string( 1 ) eq 'e1-e7' } @legal_actions;
is( @actions, 1, "[action-1] e1-e7 legal" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e2', 0, 0 );
is ( @{ $links_ref }, 1, "[action-1] e2 links count" );
is( $links_ref->[0], 'e1', "[action-1] e2 linked to e7" );
is( @{ $tentative_links_ref }, 0, "[action-1] e2 tentative links count" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e5', 0, 0 );
is( @{ $links_ref }, '0', "[action-1] e5 links count" );
is( @{ $tentative_links_ref }, 0, "[action-1] e5 tentative links count" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'e7', 0 );
@actions = grep { $_->to_string( 1 ) eq 'e7-e1' } @legal_actions;
is( @actions, 1, "[action-2] e7-e1 legal" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e2', 0, 0 );
is( @{ $links_ref }, 0, "[action-2] e2 links count" );
is( @{ $tentative_links_ref }, 0, "[action-2] e2 tentative links count" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e5', 0, 0 );
is( @{ $links_ref }, 1, "[action-2] e5 links count" );
is( $links_ref->[0], 'e7', "[action-2] e5 linked to e7" );
is( @{ $tentative_links_ref }, 0, "[action-2] e5 tentative links count" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'e2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'e2-e3' } @legal_actions;
is( @actions, 1, "[action-3] e2-e3 legal" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'e2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'e2-e4' } @legal_actions;
is( @actions, 1, "[action-4] e2-e6 legal" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'e4', 0 );
isnt( @legal_actions , 0, "[action-5] legal actions" );
@actions = grep { $_->to_string( 1 ) eq 'e4-f4' } @legal_actions;
is ( @actions, 1, "[action-5] e4-f4 legal" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e3', 0, 0 );
is( @{ $links_ref }, 0, "[action-5] e3 links count" );
is( @{ $tentative_links_ref }, 1, "[action-5] e3 tentative links count" );
is( $tentative_links_ref->[0], 'e4', "[action-5] e3 tentatively linked to e4" );

( $links_ref, $tentative_links_ref )
    = $board->get_fliers_linked_to_carrier( 'e5', 0, 0 );
is( @{ $links_ref }, 0, "[action-5] e5 links count" );
is( @{ $tentative_links_ref }, 1, "[action-5] e5 tentative links count" );
is( $tentative_links_ref->[0], 'e4', "[action-5] e5 tentatively linked to e4" );

################################
### Moving Linked Units Pt.2 ###
################################

print "\n*** Yet more linked unit movement testing ***\n";

# This is another test that was spawned by a bug where
# moving a carrier sometimes resulted in moving an
# unrelated gamepiece, instead of a linked flier.

#################
#   # F #   #   #
#################
#   #   #   #   #
#################
#   # B # P #   #
#################
#   #   #   #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## P = Space Pawn
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 4, 'y-tiles' => 4 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);
$space_pawn = ChessLike::GamePiece->new(
   'name'         => 'Space Pawn',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ] ],
);

$board->add(
   'b2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'c2',
   'gamepiece' => $space_pawn,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'space_pawn.gif',
);
$board->add(
   'b4',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'flying_queen.gif',
);

@legal_actions = $board->legal_actions( 'b4', 0 );
@actions = grep { $_->to_string( 1 ) eq 'b4-c3' } @legal_actions;
is( @actions, 1, "[action-1] b4-c3 legal" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'c2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c2-c3' } @legal_actions;
is( @actions, 1, "[action-2] c2-c3 legal" );

$board->apply_action( $actions[0] );

@legal_actions = $board->legal_actions( 'b2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'b2-a2' } @legal_actions;
is( @actions, 1, "[action-3] b2-a2 legal" );

$board->apply_action( $actions[0] );

$gamepiece = $board->get_tile_contents_for_index(
    'b3', 0, 'gamepiece' );

is( $gamepiece->name(), 'Flying Queen',
    '[action-3] correct gamepiece linked' );

################################
### Moving Linked Units Pt.3 ###
################################

print "\n*** Even more linked unit movement testing ***\n";

# This is a test that was spawned by a bug where
# moving fliers with a carrier sometimes resulted
# in the old flier position not being completey 
# forgotten in the board's internal flier-cache.
# The result was that this specific sequence of
# moves would result in the board trying to access
# an additional phantom flier located where a
# flier (in this case, the docked flier) used to
# be a turn prior.

#################
#   #   #   #   #
#################
#   #   #   #   #
#################
#   # F # BF#   #
#################
#   #   #   #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 4, 'y-tiles' => 4 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);

$board->add(
   'b2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'c2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'c2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'flying_queen.gif',
);

@legal_actions = $board->legal_actions( 'c2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c2-c3' } @legal_actions;
is( @actions, 1, '[action-1] c2-c3 legal' );

$board->apply_action( $actions[0] );

$gamepieces_ref = $board->get_tile_contents(
    'b2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-1] b2 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'c2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-1] c2 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'b3', 'gamepieces' );
is( @{ $gamepieces_ref }, 1, '[action-1] b3 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'c3', 'gamepieces' );
is( @{ $gamepieces_ref }, 2, '[action-1] c3 array size' );

@legal_actions = $board->legal_actions( 'c3', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c3-c4' } @legal_actions;
is( @actions, 1, '[action-2] c3-c4 legal' );

lives_ok { $board->apply_action( $actions[0] ) }
    '[action-2] apply_action() succeeds';

$gamepieces_ref = $board->get_tile_contents(
    'b2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-2] b2 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'c2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-2] c2 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'b3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-2] b3 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'c3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[action-2] c3 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'b4', 'gamepieces' );
is( @{ $gamepieces_ref }, 1, '[action-2] b4 array size' );

$gamepieces_ref = $board->get_tile_contents(
    'c4', 'gamepieces' );
is( @{ $gamepieces_ref }, 2, '[action-2] c4 array size' );

##############################
### Linked Unit Subtleties ###
##############################

print "\n*** Linked unit subtleties ***\n";

# This is where we make sure that some of the subtle rules
# are enforced:
#   1) Enemy fliers can occupy the same tile as a docked flier
#   2) Enemy fliers can move through a tile with a docked flier
#   3) A docked unit on one carrier cannot be shared with another

#################
#   #   #   #   #
#################
#   #   #   #   #
#################
# BF# BF# f #   #
#################
#   #   # b #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 4, 'y-tiles' => 4 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'shot-vectors' => [ [ 0, 0, 1 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);

$board->add(
   'c1',
   'gamepiece' => $base,
   'direction' => 'w',
   'team'      => 1,
   'graphic'   => 'base.gif',
);

$board->add(
   'a2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'a2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'b2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'b2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 0,
   'graphic'   => 'base.gif',
);
$board->add(
   'c2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'base.gif',
);

@legal_actions = $board->legal_actions( 'c2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c2-b2' } @legal_actions;
is( @actions, 1, "[legal move] flier tries to coexist with an enemy, docked flier" );

$action = $actions[0];  # We like this one

@legal_actions = $board->legal_actions( 'c2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c2-a2' } @legal_actions;
is( @actions, 1, "[legal move] flier tries to pass through an enemy, docked flier" );

$board->apply_action( $action );  # The coexist action

$action = ChessLike::Action->new(
    'tile' => 'b2',
    'index' => 0,
    'type' => 'move',
    'target-tile' => 'b1',
    'target-index' => -1,
    'direction' => undef,
    'links' => [ 'b2.1', 'a2.1' ]
);

dies_ok { $board->apply_action( $action ) }
    "[illegal move] tried to share a docked flier";

@legal_actions = $board->legal_actions( 'b2', 1 );
@actions = grep { $_->to_string( 1 ) eq 'b2.1*b2.2' } @legal_actions;
is( @actions, 1, "[sanity check] docked flier tries to shoot enemy flier on same tile." );


#######################################
### Rotating with Linked Units Pt.1 ###
#######################################

print "\n*** Rotating with linked units ***\n";

# Here we make sure that rotating carriers flip their linked
# fliers appropriately.

#################
#   #   #   #   #
#################
#   #   #   #   #
#################
#   #   # F # B #
##############v##
#   #   #   #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 4, 'y-tiles' => 4 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'shot-vectors' => [ [ 0, 0, 1 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);

$board->add(
   'c2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'd2',
   'gamepiece' => $base,
   'direction' => 's',
   'team'      => 1,
   'graphic'   => 'base.gif',
);

@legal_actions = $board->legal_actions( 'd2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'd2-d3' } @legal_actions;
is( @actions, 1, "[legal move] d2-d3" );

$action = $actions[0];
$action->set_direction( 'w' );

$board->apply_action( $action );  # d2-d3>w

( $gamepieces_ref, $directions_ref ) =
    $board->get_tile_contents( 'd3', 'gamepieces', 'directions' );
is( @{ $gamepieces_ref }, 1, '[ d2-d3>w ] d3: count' );
is( $gamepieces_ref->[0]->name(), 'Base',
    '[ d2-d3>w ] d3: name' );
is( $directions_ref->[0], 'w',
    '[ d2-d3>w ] d3: direction' );

$gamepieces_ref = $board->get_tile_contents( 'd4', 'gamepieces' );
is( @{ $gamepieces_ref }, 1, '[ d2-d3>w ] d4: count' );
is( $gamepieces_ref->[0]->name(), 'Flying Queen',
    '[ d2-d3>w ] c3: name' );
$gamepieces_ref = $board->get_tile_contents( 'a3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>w ] a3: count' );
$gamepieces_ref = $board->get_tile_contents( 'd2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>w ] d2: count' );
$gamepieces_ref = $board->get_tile_contents( 'c3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>w ] c3: count' );

#################
#   #   #   # F #
#################
#   #   #   #<B #
#################
#   #   #   #   #
#################
#   #   #   #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

@legal_actions = $board->legal_actions( 'd3', 0 );
@actions = grep { $_->to_string( 1 ) eq 'd3-d2' } @legal_actions;
is( @actions, 1, "[legal move] d3-d2" );

$action = $actions[0];
$action->set_direction( 'e' );

$board->apply_action( $action );  # d3-d2>e

( $gamepieces_ref, $directions_ref ) =
    $board->get_tile_contents( 'd2', 'gamepieces', 'directions' );
is( @{ $gamepieces_ref }, 1, '[ d3-d2>e ] d2: count' );
is( $gamepieces_ref->[0]->name(), 'Base',
    '[ d3-d2>e ] d2: name' );
is( $directions_ref->[0], 'e',
    '[ d3-d2>e ] d2: direction' );

$gamepieces_ref = $board->get_tile_contents( 'd3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>e ] d3: count' );
$gamepieces_ref = $board->get_tile_contents( 'a2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>e ] a2: count' );
$gamepieces_ref = $board->get_tile_contents( 'd1', 'gamepieces' );
is( @{ $gamepieces_ref }, 1, '[ d3-d2>e ] d1: count' );
is( $gamepieces_ref->[0]->name(), 'Flying Queen',
    '[ d3-d2>e ] c3: name' );
$gamepieces_ref = $board->get_tile_contents( 'c2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>e ] c2: count' );

#################
#   #   #   #   #
#################
#   #   #   #   #
#################
#   #   #   # B>#
#################
#   #   #   # F #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

@legal_actions = $board->legal_actions( 'd2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'd2-d3' } @legal_actions;
is( @actions, 1, "[legal move] d2-d3" );

$action = $actions[0];
$action->set_direction( 'n' );

$board->apply_action( $action );  # d2-d3>n

( $gamepieces_ref, $directions_ref ) =
    $board->get_tile_contents( 'd3', 'gamepieces', 'directions' );
is( @{ $gamepieces_ref }, 2, '[ d2-d3>n ] d3: count' );
is( $gamepieces_ref->[0]->name(), 'Base',
    '[ d2-d3>n ] d3.0: name' );
is( $directions_ref->[0], 'n',
    '[ d2-d3>n ] d3.0: direction' );
is( $gamepieces_ref->[1]->name(), 'Flying Queen',
    '[ d2-d3>n ] d3.1: name' );

$gamepieces_ref = $board->get_tile_contents( 'd4', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>n ] d4: count' );
$gamepieces_ref = $board->get_tile_contents( 'a3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>n ] a3: count' );
$gamepieces_ref = $board->get_tile_contents( 'd2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>n ] d2: count' );
$gamepieces_ref = $board->get_tile_contents( 'c3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d2-d3>n ] c3: count' );

#################
#   #   #   #   #
##############^##
#   #   #   # BF#
#################
#   #   #   #   #
#################
#   #   #   #   #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

@legal_actions = $board->legal_actions( 'd3', 0 );
@actions = grep { $_->to_string( 1 ) eq 'd3-d2' } @legal_actions;
is( @actions, 1, "[legal move] d3-d2" );

$action = $actions[0];
$action->set_direction( 's' );

$board->apply_action( $action );  # d3-d2>s

( $gamepieces_ref, $directions_ref ) =
    $board->get_tile_contents( 'd2', 'gamepieces', 'directions' );
is( @{ $gamepieces_ref }, 2, '[ d3-d2>s ] d2: count' );
is( $gamepieces_ref->[0]->name(), 'Base',
    '[ d3-d2>s ] d2.0: name' );
is( $directions_ref->[0], 's',
    '[ d3-d2>s ] d2.0: direction' );
is( $gamepieces_ref->[1]->name(), 'Flying Queen',
    '[ d3-d2>s ] d2.1: name' );

$gamepieces_ref = $board->get_tile_contents( 'd3', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>s ] d3: count' );
$gamepieces_ref = $board->get_tile_contents( 'a2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>s ] a2: count' );
$gamepieces_ref = $board->get_tile_contents( 'd1', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>s ] d1: count' );
$gamepieces_ref = $board->get_tile_contents( 'c2', 'gamepieces' );
is( @{ $gamepieces_ref }, 0, '[ d3-d2>s ] c2: count' );

#######################################
### Rotating with Linked Units Pt.2 ###
#######################################

print "\n*** More rotating with linked units ***\n";

# Here we make sure that carriers cannot rotate if
# one of their linked fliers will occupy the same
# space as an enemy flier.  We run this test for
# both shared and unshared links.

#################
#   #   #   #   #
##############^##
#   #   #   # B #
##########^######
# f # F # B # F #
##^###########^##
# b #   # f # b #
#################
## Uppercase = Player1
## Lowercase = Player2
## B = Base
## F = Flying Queen

$board = ChessLike::Board->new( 'x-tiles' => 4, 'y-tiles' => 4 );
$base = ChessLike::GamePiece->new(
   'name'         => 'Base',
   'move-vectors' => [ [ -1, 1, 1 ], [ 0, 1, 1 ], [ 1, 1, 1 ],
                       [ -1, 0, 1 ],              [ 1, 0, 1 ],
                       [ -1,-1, 1 ], [ 0,-1, 1 ], [ 1,-1, 1 ] ],
   'can-rotate'   => 1,
   'proxy-move-field'   => [ 3, 4, 2, 2 ],
   'carries-units' => 'Flying Queen',
);
$flying_queen = ChessLike::GamePiece->new(
   'name'         => 'Flying Queen',
   'move-vectors' => [ [ -1, 1, 99 ], [ 0, 1, 99 ], [ 1, 1, 99 ],
                       [ -1, 0, 99 ],               [ 1, 0, 99 ],
                       [ -1,-1, 99 ], [ 0,-1, 99 ], [ 1,-1, 99 ] ],
   'shot-vectors' => [ [ 0, 0, 1 ] ],
   'is-flying'    => 1,
   'requires-units'    => 'Base',
);

$board->add(
   'a1',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 2,
   'graphic'   => 'base.gif',
);
$board->add(
   'c1',
   'gamepiece' => $flying_queen,
   'direction' => 's',
   'team'      => 2,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'd1',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 2,
   'graphic'   => 'base.gif',
);
$board->add(
   'a2',
   'gamepiece' => $flying_queen,
   'direction' => 's',
   'team'      => 2,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'b2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'c2',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'base.gif',
);
$board->add(
   'd2',
   'gamepiece' => $flying_queen,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'flying_queen.gif',
);
$board->add(
   'd3',
   'gamepiece' => $base,
   'direction' => 'n',
   'team'      => 1,
   'graphic'   => 'base.gif',
);

@legal_actions = $board->legal_actions( 'c2', 0 );
@actions = grep { $_->to_string( 1 ) eq 'c2-b2' } @legal_actions;
is( @actions, 1, "[legal move] c2-b2" );

$action = $actions[0];
$action->set_direction( 'n' );

dies_ok { $board->apply_action( $action ) }
    "[illegal move] unshared link collides with enemy flier";

@actions = grep { $_->to_string( 1 ) eq 'c2>' } @legal_actions;
is( @actions, 1, "[legal move] c2>" );

$action = $actions[0];
$action->upgrade_link( 'd2' );
$action->set_direction( 'e' );

dies_ok { $board->apply_action( $action ) }
    "[illegal rotation] shared link collides with enemy flier";
